/*
 * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baomidou.mybatisplus.advance.injector.processor;

import com.baomidou.mybatisplus.advance.injector.ASTBuilder;
import com.baomidou.mybatisplus.advance.injector.OperatorConstant;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Names;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 生成操作方法
 *
 * @author wanglei
 * @since 2022-03-14
 */
public class GenOperatorMethodProcessor implements MybatisPlusAPTProcessor {
    /**
     * 沒有参数的方法
     */
    private static Set<String> noneParamMethods = new HashSet<>();

    /**
     * 一个参数的方法
     */
    private static Map<String, String> oneParamMethods = new HashMap<>();

    /**
     * 二个参数的方法
     */
    private static Set<String> towParamMethods = new HashSet<>();



    /**
     * 集合的方法
     */
    private static Set<String> collectionParamMethods = new HashSet<>();



    static {
        oneParamMethods.put(OperatorConstant.EQ, "java.lang.Comparable");
        oneParamMethods.put(OperatorConstant.NE, "java.lang.Comparable");
        oneParamMethods.put(OperatorConstant.GE, "java.lang.Comparable");
        oneParamMethods.put(OperatorConstant.GT, "java.lang.Comparable");
        oneParamMethods.put(OperatorConstant.LE, "java.lang.Comparable");
        oneParamMethods.put(OperatorConstant.LT, "java.lang.Comparable");
        oneParamMethods.put(OperatorConstant.LIKE, "java.lang.String");
        oneParamMethods.put(OperatorConstant.LIKE_LEFT, "java.lang.String");
        oneParamMethods.put(OperatorConstant.LIKE_RIGHT, "java.lang.String");
        oneParamMethods.put(OperatorConstant.NOT_LIKE, "java.lang.String");
        noneParamMethods.add(OperatorConstant.IS_NULL);
        noneParamMethods.add(OperatorConstant.NOT_NULL);
        noneParamMethods.add(OperatorConstant.ORDER_BY_ASC);
        noneParamMethods.add(OperatorConstant.ORDER_BY_DESC);
        collectionParamMethods.add(OperatorConstant.IN);
        collectionParamMethods.add(OperatorConstant.NOT_IN);
        towParamMethods.add(OperatorConstant.BETWEEN);
        towParamMethods.add(OperatorConstant.NOT_BETWEEN);
    }

    @Override
    public void generate(ASTBuilder astBuilder) {
        if (!astBuilder.hasField("actions")) {
            astBuilder.addField(genActionsField(astBuilder));
            astBuilder.addMethod(genChildActionMethod(astBuilder));
        }
        //没有参数的方法
        for (String operatorMethod : noneParamMethods) {
            if (!astBuilder.hasMethod(operatorMethod)) {
                astBuilder.addMethod(genMethod(astBuilder.getJcClassDecl(),
                        false, operatorMethod, null, astBuilder));
            }
        }
        // 添加一个参数的方法
        for (String operatorMethod : oneParamMethods.keySet()) {
            if (!astBuilder.hasMethod(operatorMethod)) {
                astBuilder.addMethod(genMethod(astBuilder.getJcClassDecl(),
                        true, operatorMethod, oneParamMethods.get(operatorMethod), astBuilder));
            }
        }
        //2个参数方法
        for (String operatorMethod : towParamMethods) {
            if (!astBuilder.hasMethod(operatorMethod)) {
                astBuilder.addMethod(gen2ParamMethod(operatorMethod, astBuilder));
            }
        }
        //集合参数方法
        for (String operatorMethod : collectionParamMethods) {
            if (!astBuilder.hasMethod(operatorMethod)) {
                astBuilder.addMethod(genParamCollectionMethod(operatorMethod, astBuilder));
            }
        }
    }


    /**
     * 构造2个参数或者没有参数的方法
     *
     * @param methodName 方法名
     * @param astBuilder    構造器
     * @return
     */
    private JCTree.JCMethodDecl gen2ParamMethod( String methodName,ASTBuilder astBuilder) {
        Names names = astBuilder.getNames();
        TreeMaker treeMaker = astBuilder.getTreeMaker();
        // 生成 调用方法的参数列表
        List<JCTree.JCExpression> execFunparamsList = List.nil();
        // 参数添加this.wrapperVars
        execFunparamsList = execFunparamsList.append(treeMaker.Select(
                treeMaker.Ident(names.fromString("this"))
                , names.fromString("actions")
        ));
        // 操作符
        execFunparamsList = execFunparamsList.append(treeMaker.Literal(methodName));

        // po
        execFunparamsList = execFunparamsList.append(treeMaker.Ident(names.fromString("this")));

        //属性名
        execFunparamsList = execFunparamsList.append(treeMaker.Select(
                treeMaker.Ident(names.fromString("this"))
                , names.fromString("preFieldName")
        ));

        //变量min
        execFunparamsList = execFunparamsList.append(treeMaker.Ident(names.fromString( "min")));

        execFunparamsList = execFunparamsList.append(treeMaker.Ident(names.fromString( "max")));

        //参数值列表
        List<JCTree.JCVariableDecl> genMethodParamsList = List.nil();

        //方法参数: 方法的形式参数
        JCTree.JCVariableDecl paramMin = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER, List.nil()),  //访问变量标识符
                names.fromString( "min"),        //变量名
                astBuilder.typeRef("java.lang.Comparable"),     //变量类型
                null        //变量初始值
        );

        //方法参数: 方法的形式参数
        JCTree.JCVariableDecl paramMax = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER, List.nil()),  //访问变量标识符
                names.fromString( "max"),        //变量名
                astBuilder.typeRef("java.lang.Comparable"),     //变量类型
                null        //变量初始值
        );
        genMethodParamsList = List.of(paramMin).append(paramMax);

        //生成return语句: return this;
        JCTree.JCReturn returnStatement = treeMaker.Return(
                treeMaker.Ident(names.fromString("this"))
        );
        String execMethodAllName = "com.baomidou.mybatisplus.advance.injector.FuntionTools.addAction";
        return astBuilder.genMethod(
                execFunparamsList
                , genMethodParamsList,
                new String[]{"java.util.List",
                        "java.lang.String", "java.lang.Object",
                        "java.lang.String", "java.lang.Object", "java.lang.Object"},
                treeMaker.Ident(astBuilder.getJcClassDecl().getSimpleName()), returnStatement, execMethodAllName,
                names.fromString(methodName));
    }


    /**
     * 添加action字段
     *
     * @param astBuilder
     * @return
     */
    private JCTree.JCVariableDecl genActionsField(ASTBuilder astBuilder) {
        return astBuilder.genField( "actions", "java.util.ArrayList",
                astBuilder.newObject("java.util.ArrayList",List.nil()));
    }

    /**
     * 添加childAction方法
     *
     * @param astBuilder astBuilder
     * @return childAction方法
     */
    private JCTree.JCMethodDecl genChildActionMethod(ASTBuilder astBuilder) {
        Names names = astBuilder.getNames();
        TreeMaker treeMaker = astBuilder.getTreeMaker();
        //生成return语句: return this;
        JCTree.JCReturn returnStatement = treeMaker.Return(
                treeMaker.Select(
                        treeMaker.Ident(names.fromString("this"))
                        , names.fromString("actions")
                )
        );
        return astBuilder.genMethod(null
                , List.nil(),
                null,
                astBuilder.typeRef("java.util.List"),
                returnStatement, null, names.fromString("childAction"));
    }



    /**
     * 构造集合 参数的方法 返回this
     *
     * @param methodName 方法名
     * @param astBuilder
     * @return
     */
    private JCTree.JCMethodDecl genParamCollectionMethod(String methodName, ASTBuilder astBuilder) {
        JCTree.JCClassDecl jcClassDecl = astBuilder.getJcClassDecl();
        // 生成 调用方法的参数列表
        List<JCTree.JCExpression> execFunparamsList = List.nil();
        Names names = astBuilder.getNames();
        TreeMaker treeMaker = astBuilder.getTreeMaker();
        // 参数添加this.wrapper
        execFunparamsList = execFunparamsList.append(treeMaker.Select(
                treeMaker.Ident(names.fromString("this"))
                , names.fromString("actions")
        ));

        // 操作符
        execFunparamsList = execFunparamsList.append(treeMaker.Literal(methodName));

        // po
        execFunparamsList = execFunparamsList.append(treeMaker.Ident(names.fromString("this")));

        //属性名
        execFunparamsList = execFunparamsList.append(treeMaker.Select(
                treeMaker.Ident(names.fromString("this"))
                , names.fromString("preFieldName")
        ));

        //变量
        execFunparamsList = execFunparamsList.append(treeMaker.Ident(names.fromString("collection")));



        //方法参数: 方法的形式参数 Collection<?> collection
        JCTree.JCVariableDecl param = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER, List.nil()),  //访问变量标识符
                names.fromString("collection"),        //变量名
                astBuilder.typeRef("java.util.Collection"),     //变量类型
                null        //变量初始值
        );

        List<JCTree.JCVariableDecl> genMethodParamsList = List.of(param);

        //生成return语句: return this;
        JCTree.JCReturn returnStatement = treeMaker.Return(
                treeMaker.Ident(names.fromString("this"))
        );
        String execMethodAllName = "com.baomidou.mybatisplus.advance.injector.FuntionTools.addAction";
        return astBuilder.genMethod(
                execFunparamsList
                , genMethodParamsList,
                new String[]{"java.util.List", "java.lang.String",
                        "java.lang.Object", "java.lang.String", "java.lang.Object"},
                treeMaker.Ident(jcClassDecl.getSimpleName()), returnStatement, execMethodAllName, names.fromString(methodName));
    }


    /**
     * 构造单个参数或者没有参数的方法
     *
     * @param jcClassDecl 类
     * @param isNeedParam 是否需要参数
     * @param methodName  方法名
     * @param astBuilder
     * @return
     */
    private JCTree.JCMethodDecl genMethod(JCTree.JCClassDecl jcClassDecl,
                                          boolean isNeedParam, String methodName, String varType, ASTBuilder astBuilder) {
        Names names = astBuilder.getNames();
        TreeMaker treeMaker = astBuilder.getTreeMaker();
        // 生成 调用方法的参数列表
        List<JCTree.JCExpression> execFunparamsList = List.nil();
        // 参数添加this.wrapperVars
        execFunparamsList = execFunparamsList.append(treeMaker.Select(
                treeMaker.Ident(names.fromString("this"))
                , names.fromString("actions")
        ));
        // 操作符
        execFunparamsList = execFunparamsList.append(treeMaker.Literal(methodName));

        // po
        execFunparamsList = execFunparamsList.append(treeMaker.Ident(names.fromString("this")));

        //属性名
        execFunparamsList = execFunparamsList.append(treeMaker.Select(
                treeMaker.Ident(names.fromString("this"))
                , names.fromString("preFieldName")
        ));

        //变量
        execFunparamsList = execFunparamsList.append(isNeedParam ? treeMaker.Ident(names.fromString("args")) : treeMaker.Literal(true));

        //参数值列表
        List<JCTree.JCVariableDecl> genMethodParamsList = List.nil();
        if (isNeedParam) {
            //方法参数: 方法的形式参数
            JCTree.JCVariableDecl param = treeMaker.VarDef(
                    treeMaker.Modifiers(Flags.PARAMETER, List.nil()),  //访问变量标识符
                    names.fromString("args"),        //变量名
                    astBuilder.typeRef(varType),     //变量类型
                    null        //变量初始值
            );

            genMethodParamsList = List.of(param);
        }

        //生成return语句: return this;
        JCTree.JCReturn returnStatement = treeMaker.Return(
                treeMaker.Ident(names.fromString("this"))
        );
        String execMethodAllName = "com.baomidou.mybatisplus.advance.injector.FuntionTools.addAction";
        return astBuilder.genMethod(
                execFunparamsList
                , genMethodParamsList,
                new String[]{"java.util.List",
                        "java.lang.String", "java.lang.Object",
                        "java.lang.String", "java.lang.Object"},
                treeMaker.Ident(jcClassDecl.getSimpleName()), returnStatement, execMethodAllName, names.fromString(methodName));
    }




}
